{-# LANGUAGE TypeFamilies               #-}
{-# OPTIONS -Wall #-}

--------------------------------------------------------------------------------
-- |
-- Module      :  Majalan.Pitch.Centaur.Base
-- Copyright   :  (c) Stephen Tetley 2014
-- License     :  BSD3
--
-- Maintainer  :  stephen.tetley@gmail.com
-- Stability   :  unstable
-- Portability :  GHC
--
-- Kraig Grady\'s 12-tone Centaur scale.
--
--------------------------------------------------------------------------------

module Majalan.Pitch.Centaur.Base
  ( 

    Centaur

  , makeCentaur

  , centaurToOctave

  ) where

-- Note - this representation avoids pitch (and interval) naming
-- as we are only generating numerical values...

import Majalan.Base.Internal.Base
import Majalan.Base.Internal.PitchUtils

import Data.AffineSpace                         -- package: vector-space




-- | Pitch-class representation - octave and pitch-class
-- 
data Centaur = Centaur Int Z12
  deriving (Eq,Ord)

instance Show Centaur where
  showsPrec _ (Centaur o i) = shows o . showChar '.' . fn i
    where
      fn n | n < 10    = showChar '0' . shows n
           | otherwise = shows n 
 

instance TPitch Centaur where
  toHertz gblhz = octaveToHertz gblhz . centaurToOctave


makeCentaur :: Int -> Int -> Centaur
makeCentaur o i = Centaur o (toZ12 i)



centaurToOctave :: Centaur -> Octave
centaurToOctave (Centaur o i) = y + (fn i - 1.0)
  where
    y = fromIntegral o  
    fn  0 = 1/1
    fn  1 = 21/20
    fn  2 = 9/8
    fn  3 = 7/6
    fn  4 = 5/4
    fn  5 = 4/3
    fn  6 = 7/5 
    fn  7 = 3/2
    fn  8 = 14/9
    fn  9 = 5/3
    fn 10 = 7/4
    fn 11 = 15/8
    fn  _ = error "Centaur - unreachable (modolu 12)"



midiToCentaur :: MidiPitch -> Centaur
midiToCentaur mp = Centaur (o+1) (fromIntegral i)
  where
    (o,i) = fromIntegral mp `divMod` 12

centaurToMidi :: Centaur -> MidiPitch
centaurToMidi (Centaur o i) = fromIntegral $ fromIntegral i + ove
  where
    ove = 12 * (o-1)


pchDiff :: Centaur -> Centaur -> Interval
pchDiff a b = centaurToMidi a .-. centaurToMidi b


-- Simplest addtion (subtraction...) is to go via an 
-- integer representation aka midi.
--
semitoneAdd :: Centaur -> Interval -> Centaur
semitoneAdd pch iv = midiToCentaur $ (.+^ iv) $ centaurToMidi pch



instance AffineSpace Centaur where
  type Diff Centaur = Interval
  (.-.) = pchDiff
  (.+^) = semitoneAdd


